﻿//AcademicPlanner - course registration planning web app.
//Copyright (C) 2009  Boubacar Diallo

//This program is free software; you can redistribute it and/or
//modify it under the terms of the GNU General Public License
//as published by the Free Software Foundation; either version 2
//of the License, or (at your option) any later version.

//This program is distributed in the hope that it will be useful,
//but WITHOUT ANY WARRANTY; without even the implied warranty of
//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//GNU General Public License for more details.

//You should have received a copy of the GNU General Public License
//along with this program; if not, write to the Free Software
//Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.


using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using AcademicPlanner.Data;

namespace AcademicPlanner.Domain
{
	/// <summary>
	/// ModelCollection(TModel, TEntityRepo, TREntity) is the base for collections
	/// of <c>Model</c> objects.
	/// </summary>
	/// <typeparam name="TModel">The type of the <c>Model</c>s of this collection.</typeparam>
	/// <typeparam name="TEntityRepo">
	/// The type of the <c>EntityRepository(TEntity)</c> of this collection's <c>Model</c>s' underlying <c>Entity</c>.
	/// </typeparam>
	/// <typeparam name="TREntity">The type of this collection's <c>Model</c>s' underlying <c>Entity</c>.</typeparam>
	/// <since>1.0</since>
	public abstract class ModelCollection<TModel, TEntityRepo, TREntity>
		where TModel : Model
		where TREntity : Entity
		where TEntityRepo : EntityRepository<TREntity>
	{
		/// <summary>
		/// The underlying <c>Entity</c>s of this collection's <c>Model</c>s.
		/// </summary>
		/// <since>1.0</since>
		protected ICollection entities;

		//protected uint itemsPerPage;

		//protected uint pageNumber;


		/// <summary>
		/// Gets the <c>TEntityRepo</c> of the underlying <c>Entity</c>s of this collection's <c>Model</c>s.
		/// </summary>
		/// <since>1.0</since>
		protected abstract TEntityRepo EntityRepo { get; }

		/// <summary>
		/// Gets the <c>DomainMaster</c> of the AcademicPlanner application.
		/// </summary>
		/// <since>1.0</since>
		protected DomainMaster DomainMaster { get { return DomainMaster.GetInstance(); } }


		/// <summary>
		/// No argument constructor; does nothing.
		/// </summary>
		/// <since>1.0</since>
		public ModelCollection()
		{

		}

		/// <summary>
		/// Constructs a ModelCollection object and sets its <c>entities</c> to the given
		/// collection of <c>TREntity</c>s.
		/// </summary>
		/// <param name="pEntities">
		/// The collection of <c>TREntity</c>s to initialize this ModelCollection with.
		/// </param>
		/// <since>1.0</since>
		internal ModelCollection(ICollection pEntities)
		{
			entities = pEntities;
		}

		//internal ModelCollection(ICollection pEntities, uint pPageNumber, uint pItemsPerPage)
		//    : this(pEntities)
		//{
		//    pageNumber = pPageNumber;
		//    itemsPerPage = pItemsPerPage;
		//}

		/// <summary>
		/// Constructs a ModelCollection object and sets its <c>entities</c> to the given
		/// list of <c>TREntity</c>s.
		/// </summary>
		/// <param name="pEntities">The list of <c>TREntity</c>s to initialize this ModelCollection with.</param>
		/// <since>1.0</since>
		internal ModelCollection(IList<TREntity> pEntities)
		{
			entities = (ICollection)pEntities;
		}


		/// <summary>
		/// Converts this ModelCollection to an <c>IList</c> of <c>TModel</c>s.
		/// This method basically creates the <c>Model</c>s of each of its <c>TREntity</c>s.
		/// </summary>
		/// <returns>The list of <c>TModel</c>s.</returns>
		/// <seealso cref="P:DomainMaster.ModelsFountain"/>
		/// <seealso cref="M:ModelsFountain.GetModel(TModel)"/>
		/// <since>1.0</since>
		public IList<TModel> ToList()
		{
			IList<TModel> rList = new List<TModel>();
			var vEntitiesList = entities as IList<TREntity>;
			
			if (entities != null && ((ICollection)vEntitiesList) != null)
			{
				foreach (var vEnt in vEntitiesList)
				{
					if (!vEnt.Trashed)
						rList.Add(DomainMaster.ModelsFountain.GetModel<TModel>(vEnt));
				}
			}

			return rList;
		}

		//public ModelCollectionPage ToPage()
		//{
		//    return new ModelCollectionPage(this);
		//}

		/// <summary>
		/// Creates a <c>ModelCollectionPage</c> to represent a page
		/// from this ModelCollection.
		/// </summary>
		/// <param name="pPageNumber">The page number.</param>
		/// <param name="pItemsPerPage">The number of items per page.</param>
		/// <returns>The <c>ModelCollectionPage</c>.</returns>
		/// <since>1.0</since>
		public ModelCollectionPage ToPage(uint pPageNumber, uint pItemsPerPage)
		{
			return new ModelCollectionPage(this, pPageNumber, pItemsPerPage);
		}

		/// <summary>
		/// Filters this ModelCollection to return only the <c>Model</c> with the given
		/// <c>Entity</c> identifier. This method relies on this ModelCollection's 
		/// <see cref="P:EntityRepo"/> to find the matching <c>Entity</c> then creates
		/// the <c>Model</c> represented by that <c>Entity</c>.
		/// </summary>
		/// <param name="pEntityId">The identifier whose matching <c>Model</c>to return.</param>
		/// <returns>The <c>Model</c> matching the given identifier or <c>null</c>.</returns>
		/// <exception cref="T:CriticalDomainException">
		/// Thrown with an error code of <c>DomainErrorCode.M_ENTITY_RETRIEVAL_BY_ID_FAILURE</c>
		/// when a <see cref="T:AcademicPlanner.Data.DataException"/> occurs;
		/// the <see cref="T:AcademicPlanner.Data.DataException"/> is accessible through the
		/// <c>InnerException</c> property.
		/// </exception>
		/// <seealso cref="P:DomainMaster.ModelsFountain"/>
		/// <seealso cref="M:ModelsFountain.GetModel(TModel)"/>
		/// <seealso cref="P:EntityRepo"/>
		/// <seealso cref="M:AcademicPlanner.Data.EntityRepository(TEntity).FindById"/>
		/// <since>1.0</since>
		public TModel WithId(int pEntityId)
		{
			TModel rMod = null;
			TREntity vEnt = null;

			try
			{
				if (entities != null)
					vEnt = EntityRepo.FilterById(entities, pEntityId);
				else
					vEnt = EntityRepo.FindById(pEntityId);
			}
			catch (DataException vDx)
			{
				var vCx = new CriticalDomainException(DomainErrorCode.M_ENTITY_RETRIEVAL_BY_ID_FAILURE, vDx);
				throw vCx;
			}

			if (vEnt != null)
				rMod = DomainMaster.ModelsFountain.GetModel<TModel>(vEnt);

			return rMod;
		}

		//public ModelCollection<TModel, TEntityRepo, TREntity> ForPage(uint pPageNumber, uint pItemsPerPage)
		//{
		//    ModelCollection<TModel, TEntityRepo, TREntity> rCollection = null;

		//    try
		//    {
		//        rCollection = (ModelCollection<TModel, TEntityRepo, TREntity>)
		//                        Activator.CreateInstance(this.GetType(), entities, pPageNumber, pItemsPerPage);
		//    }
		//    catch (Exception vX)
		//    {
		//        throw new CriticalDomainException(DomainErrorCode.M_ENTITY_COLLECTION_PAGINATION_SETUP_FAILURE, vX);
		//    }

		//    return rCollection;
		//}


		/// <summary>
		/// ModelCollectionPage represents a page of the items in a ModelCollection.
		/// </summary>
		/// <since>1.0</since>
		public class ModelCollectionPage
		{
			ModelCollection<TModel, TEntityRepo, TREntity> collection;

			IList<TREntity> list;

			int itemsPerPage;

			int pageNumber;

			int skip;

			int total;

			int totalPageNumbers;


			//public int TotalItems { get { return total; } }

			/// <summary>
			/// Gets the last page number of the pagination result this
			/// ModelCollectionPage is in.
			/// </summary>
			/// <since>1.0</since>
			public int LastPageNumber { get { return totalPageNumbers; } }


			//internal ModelCollectionPage(ModelCollection<TModel, TEntityRepo, TREntity> pCollection)
			//{
			//    collection = pCollection;
			//}

			internal ModelCollectionPage(ModelCollection<TModel, TEntityRepo, TREntity> pCollection,
											uint pPageNumber, uint pItemsPerPage)
			{
				collection = pCollection;
				pageNumber = (int)pPageNumber;
				itemsPerPage = (int)pItemsPerPage;

				var vEntitiesList = collection.entities as IList<TREntity>;

				skip = (itemsPerPage != 0 && pageNumber != 0)
						? (itemsPerPage * (pageNumber - 1)) : 0;
				if (collection.entities != null && ((ICollection)vEntitiesList) != null)
				{
					list = vEntitiesList;
					total = list.Count(pEnt => !pEnt.Trashed);
				}
				else
					total = 0;

				totalPageNumbers = (total > 0 && itemsPerPage > 0) 
									? (int)Math.Ceiling((total+0.0) / (int)itemsPerPage) 
									: 1;
			}


			//public bool HasNextPage()
			//{
			//    return pageNumber > 0 && pageNumber < totalPageNumbers;
			//}

			//public bool HasPreviousPage()
			//{
			//    return pageNumber > 1 && pageNumber <= totalPageNumbers;
			//}

			/// <summary>
			/// Returns a list of the <c>TModel</c>s in this ModelCollectionPage.
			/// </summary>
			/// <returns>A list of <c>TModel</c>s</returns>
			public IList<TModel> Items()
			{
				IList<TModel> rList = new List<TModel>();

				if (list != null && itemsPerPage != 0 && pageNumber != 0)
				{
					list = new List<TREntity>(list.Where(pEnt => !pEnt.Trashed)
												.Skip((int)skip).Take((int)itemsPerPage));
					foreach (var vEnt in list)
					{
						rList.Add(collection.DomainMaster.ModelsFountain.GetModel<TModel>(vEnt));
					}
				}
				else
				 rList = collection.ToList();

				return rList;
			}
		}
	}
}
